<!-- $Id$
 *
 * Red Pitaya Oscilloscope & IST Real Probe Flow Sensor
 *
 * Modified by: Flavio Ansovini <flavio.ansovini@unige.it>
 * University of Genova - Serizio Supporto Tecnologico
 *         
 * (c) Red Pitaya  http://www.redpitaya.com
 *
 * This part of code is written in Javascript & HTML.
 * Please visit http://en.wikipedia.org/wiki/JavaScript
 *              http://en.wikipedia.org/wiki/HTML
 * for more details on the languages used herein.
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Oscilloscope & IST Fux Sensor</title>
  <link href="../assets/bootstrap-3.0.0/css/bootstrap.css" rel="stylesheet" type="text/css">
  <link href="../assets/style.css?6" rel="stylesheet" type="text/css">
  <script src="../assets/bootstrap-3.0.0/js/jquery.js"></script>
  <script src="../assets/bootstrap-3.0.0/js/bootstrap.min.js"></script>
  <script src="../assets/flot/jquery.flot.min.js"></script>
  <script src="../assets/flot/jquery.flot.selection.min.js"></script>
  <script src="../assets/flot/jquery.flot.navigate.js"></script>
  <script src="../assets/flot/jquery.flot.resize.min.js"></script>
  <script src="../assets/flot/jquery.flot.touch.js?2"></script>
  <script type="text/javascript">
  
  // Settings which can be modified
   
  var app_id = 'scope+ISTsensor';
  var root_url = '';
  //var root_url = 'http://10.0.1.221';      // Test local
  //var root_url = 'http://192.168.53.133';  // Test remote and local
  //var root_url = 'http://192.168.1.100';   // Default RedPitaya IP
  var start_app_url = root_url + '/bazaar?start=' + app_id;
  var stop_app_url = root_url + '/bazaar?stop=';
  var get_url = root_url + '/data';
  var post_url = root_url + '/data';
  
  var update_interval = 50;              // Update interval for PC, milliseconds
  var update_interval_mobdev = 500;      // Update interval for mobile devices, milliseconds 
  var request_timeout = 3000;            // Milliseconds
  var long_timeout = 20000;              // Milliseconds
  var meas_panel_dec = 5;                // Decimation for numerical measure panel to make it human readable
  var meas_panel_dec_mobdev = 1;         // Decimation for numerical measure panel for mobile devices
  var points_per_px = 5;                 // How many points per pixel should be drawn. Set null for unlimited (will disable client side decimation).
  var xdecimal_places = 2;               // Number of decimal places for the xmin/xmax values. Maximum supported are 12.
  var trigger_level_xdecimal_places = 4; // Number of decimal places for trigger level tooltip
  var range_offset = 1;                  // Percentages
    
  var xmin = -1000000;
  var xmax = 1000000;  

  var time_range_max = [130, 1000, 8, 130, 1, 8];
  var range_steps = [0.5, 1, 2, 5, 10, 20, 50, 100];
  
  var plot_options = {
    colors: ['#3276B1', '#D2322D', '#009900'],    // channel1, channel2, trigger line
    lines: { lineWidth: 1 },
    selection: { mode: 'xy' },
    zoom: { interactive: true, trigger: null },
    xaxis: { min: xmin, max: xmax },
    grid: { borderWidth: 0 },
    legend: { noColumns: 2, margin: [0, 0], backgroundColor: 'transparent' },
    touch: { autoWidth: false, autoHeight: false }
  };
  
  // Settings which should not be modified
  
  var update_timer = null;
  var zoompan_timer = null;
  var downloading = false;
  var sending = false;
  var send_que = false;
  var use_long_timeout = false;
  var trig_dragging = false;
  var touch_last_y = 0;
  var user_editing = false;
  var app_started = false;
  var last_get_failed = false;
  var refresh_counter = 0;
  var autorun = 1;
  var datasets = [];
  var plot = null;
  var params = {
    original: null,
    local: null
  };
  
  // Default parameters - posted after server side app is started 
  var def_params = {
    en_avg_at_dec: 0
  }; 
    
  // On page loaded
  
  $(function() {
    
    // Show different buttons on touch screens    
    if(window.ontouchstart === undefined) {
      $('.btn-lg').removeClass('btn-lg');
      $('#accordion .btn, .modal .btn').addClass('btn-sm');
      $('#btn_zoompan').remove();
      $('#btn_zoomin, #btn_zoomout, #btn_pan').show();
    }
    else {
      update_interval = update_interval_mobdev;
      meas_panel_dec = meas_panel_dec_mobdev;
      $('#btn_zoomin, #btn_zoomout, #btn_pan').remove();
      $('#btn_zoompan').show();
    }
    
    // Add application ID in the message from modal popup
    $('.app-id').text(app_id);
    
    // Disable all controls until the params state is loaded for the first time 
    $('input, select, button', '.container').prop('disabled', true);
    
    // Events binding for trigger controls
    
    $('#trigger_canvas').on({
      'mousedown touchstart': function(evt) {
      
        // Ignore the event if trigger source is External or mode is not Normal
        if(!params.original || params.original.trig_mode != 1 || params.original.trig_source == 2) {
          return;
        }
      
        trig_dragging = true;
        $('input, select', '#accordion').blur();
        mouseDownMove(this, evt);
        evt.preventDefault();
        return false;
      },
      'mousemove touchmove': function(evt) {
        if(! trig_dragging) {
          return;
        }
        mouseDownMove(this, evt);
        evt.preventDefault();
        return false;
      },
      'mouseup mouseout touchend': mouseUpOut
    });
    
    $('input,select', '#accordion').on('focus', function() {
      user_editing = true;
    });
    
    $('#trig_mode').on('change', function() {
      onDropdownChange($(this), 'trig_mode', true);
      
      // Autorun if trigger mode is Auto(0) or Normal(1), stop if it is Single(2).
      autorun = (params.local.trig_mode == 2 ? 0 : 1); 
      runStop();
    });
    
    $('#trig_source').on('change', function() { onDropdownChange($(this), 'trig_source'); });
    $('#trig_edge').on('change', function() { onDropdownChange($(this), 'trig_edge'); });
    
    $('#trig_level')
      .on('focus paste', function() {
        $(this).parent().addClass('input-group');
        $('#apply_trig_level').show();
      })
      .on('blur', function() {
        $('#apply_trig_level').hide();
        $(this).parent().removeClass('input-group');
        
        var tlev = parseLocalFloat($(this).val());
        if(! isNaN(tlev)) {
          params.local.trig_level = tlev;
          updateTriggerSlider(undefined, false);
          redrawPlot();
          sendParams();
        }
        else {
          $(this).val(params.local.trig_level)
        }
        user_editing = false;
      })
      .on('change', function() {
        $(this).blur();
      })
      .on('keypress', function(e) {
        if(e.keyCode == 13) {
          $(this).blur();
        }
      });
    
    // Events binding for range controls
    
    $('#range_x_minus, #range_x_plus').on('click', function() {
      var nearest = $(this).data('nearest');
      
      if(nearest && plot) {
        var options = plot.getOptions();
        var axes = plot.getAxes();
        var min = (options.xaxes[0].min !== null ? options.xaxes[0].min : axes.xaxis.min);
        var max = (options.xaxes[0].max !== null ? options.xaxes[0].max : axes.xaxis.max);
        var unit = $(this).data('unit');
        
        // Convert nanoseconds to milliseconds.
        if(unit == 'ns') {
          nearest /= 1000;
        }
        
        var center = (min + max) / 2;
        var half = nearest / 2;
        min = center - half;
        max = center + half;

        options.xaxes[0].min = min;
        options.xaxes[0].max = max;

        plot.setupGrid();
        plot.draw();
        
        params.local.xmin = min;
        params.local.xmax = max;       
        
        updateRanges();
        $(this).tooltip($(this).prop('disabled') === true ? 'hide' : 'show');
        sendParams(true);
      }
    });
    
    $('#range_y_minus, #range_y_plus').on('click', function() {
      var nearest = $(this).data('nearest');
      
      if(nearest && plot) {
        var options = plot.getOptions();
        var axes = plot.getAxes();
        var min = (options.yaxes[0].min !== null ? options.yaxes[0].min : axes.yaxis.min);
        var max = (options.yaxes[0].max !== null ? options.yaxes[0].max : axes.yaxis.max);
        var unit = $(this).data('unit');
        
        // Convert millivolts to volts.
        if(unit == 'mV') {
          nearest /= 1000;
        }
        
        var center = (min + max) / 2;
        var half = nearest / 2;
        min = center - half;
        max = center + half;
        
        options.yaxes[0].min = min;
        options.yaxes[0].max = max;

        plot.setupGrid();
        plot.draw();
               
        updateRanges();
        $(this).tooltip($(this).prop('disabled') === true ? 'hide' : 'show');
        updateTriggerSlider();
      }
    });
    
    $('#offset_x_minus, #offset_x_plus').on('click', function() {
      if(plot) {
        var direction = ($(this).attr('id') == 'offset_x_minus' ? 'left' : 'right');
        var options = plot.getOptions();
        var axes = plot.getAxes();
        var min = (options.xaxes[0].min !== null ? options.xaxes[0].min : axes.xaxis.min);
        var max = (options.xaxes[0].max !== null ? options.xaxes[0].max : axes.xaxis.max);
        var offset = (max - min) * range_offset/100;
        
        if(direction == 'left') {
          min -= offset;
          max -= offset;
        }
        else {
          min += offset;
          max += offset;
        }
        
        options.xaxes[0].min = min;
        options.xaxes[0].max = max;

        plot.setupGrid();
        plot.draw();
        
        params.local.xmin = min;
        params.local.xmax = max;
               
        updateRanges();
        sendParams(true);
      }
    });
    
    $('#offset_y_minus, #offset_y_plus').on('click', function() {
      if(plot) {
        var direction = ($(this).attr('id') == 'offset_y_minus' ? 'down' : 'up');
        var options = plot.getOptions();
        var axes = plot.getAxes();
        var min = (options.yaxes[0].min !== null ? options.yaxes[0].min : axes.yaxis.min);
        var max = (options.yaxes[0].max !== null ? options.yaxes[0].max : axes.yaxis.max);
        var offset = (max - min) * range_offset/100;
        
        if(direction == 'down') {
          min -= offset;
          max -= offset;
        }
        else {
          min += offset;
          max += offset;
        }
        
        options.yaxes[0].min = min;
        options.yaxes[0].max = max;

        plot.setupGrid();
        plot.draw();
               
        updateRanges();
        updateTriggerSlider();
      }
    });
      
    // Events binding for gain controls
    
    $('#gain_ch1_att').on('change', function() { onDropdownChange($(this), 'prb_att_ch1'); });
    $('#gain_ch1_sett').on('change', function() { onDropdownChange($(this), 'gain_ch1'); });
    $('#gain_ch2_att').on('change', function() { onDropdownChange($(this), 'prb_att_ch2'); });
    $('#gain_ch2_sett').on('change', function() { onDropdownChange($(this), 'gain_ch2'); });
    
    // Events binding for PID Controller
    
    $('#pid_IST_enable').on('change', function() { 
      params.local[this.id] = ($(this).is(':checked') ? 1 : 0);
      sendParams(); 
    });

    $('#IST_save2file').on('change', function() { 
      params.local[this.id] = ($(this).is(':checked') ? 1 : 0);
      sendParams(); 
    });
    
    // PID IST Delta Temp, Kp, Ki, Kd
    $('#pid_IST_DT')
      .on('focus paste', function() {
        $(this).parent().addClass('input-group');
        $('#apply_pid_IST_DT').show();
      })
      .on('blur', function() {
        $('#apply_pid_IST_DT').hide();
        $(this).parent().removeClass('input-group');
        
        var val = parseLocalFloat($(this).val());
        if(! isNaN(val)) {
          params.local.pid_IST_DT = val;
          sendParams();
        }
        else {
          $(this).val(params.local.pid_IST_DT)
        }
        user_editing = false;
      })
      .on('change', function() {
        $(this).blur();
      })
      .on('keypress', function(e) {
        if(e.keyCode == 13) {
          $(this).blur();
        }
      });

    $('#pid_IST_kp')
      .on('focus paste', function() {
        $(this).parent().addClass('input-group');
        $('#apply_pid_IST_kp').show();
      })
      .on('blur', function() {
        $('#apply_pid_IST_kp').hide();
        $(this).parent().removeClass('input-group');
        
        var val = parseLocalFloat($(this).val());
        if(! isNaN(val)) {
          params.local.pid_IST_kp = val;
          sendParams();
        }
        else {
          $(this).val(params.local.pid_IST_kp)
        }
        user_editing = false;
      })
      .on('change', function() {
        $(this).blur();
      })
      .on('keypress', function(e) {
        if(e.keyCode == 13) {
          $(this).blur();
        }
      });

    $('#pid_IST_ki')
      .on('focus paste', function() {
        $(this).parent().addClass('input-group');
        $('#apply_pid_IST_ki').show();
      })
      .on('blur', function() {
        $('#apply_pid_IST_ki').hide();
        $(this).parent().removeClass('input-group');
        
        var val = parseLocalFloat($(this).val());
        if(! isNaN(val)) {
          params.local.pid_IST_ki = val;
          sendParams();
        }
        else {
          $(this).val(params.local.pid_IST_ki)
        }
        user_editing = false;
      })
      .on('change', function() {
        $(this).blur();
      })
      .on('keypress', function(e) {
        if(e.keyCode == 13) {
          $(this).blur();
        }
      });

    $('#pid_IST_kd')
      .on('focus paste', function() {
        $(this).parent().addClass('input-group');
        $('#apply_pid_IST_kd').show();
      })
      .on('blur', function() {
        $('#apply_pid_IST_kd').hide();
        $(this).parent().removeClass('input-group');
        
        var val = parseLocalFloat($(this).val());
        if(! isNaN(val)) {
          params.local.pid_IST_kd = val;
          sendParams();
        }
        else {
          $(this).val(params.local.pid_IST_kd)
        }
        user_editing = false;
      })
      .on('change', function() {
        $(this).blur();
      })
      .on('keypress', function(e) {
        if(e.keyCode == 13) {
          $(this).blur();
        }
      });
      
    // Modals
    
    $('#modal_err, #modal_app').modal({ show: false, backdrop: 'static', keyboard: false });
    
    $('#btn_switch_app').on('click', function() {
      var newapp_id = $('#new_app_id').text();
      if(newapp_id.length) {
        location.href = location.href.replace(app_id, newapp_id);
      }
    });
    
    $('.btn-app-restart').on('click', function() {
      location.reload();
    });
    
    $('#btn_retry_get').on('click', function() {
      $('#modal_err').modal('hide');
      updateGraphData();
    });
    
    $('.btn-close-modal').on('click', function() {
      $(this).closest('.modal').modal('hide');
    });
    
    // Other event bindings
    
    $('#trigger_tooltip').tooltip({
      title: '',
      trigger: 'manual',
      placement: 'auto left',
      animation: false
    });
    
    $('.btn').on('click', function() {
      var btn = $(this);
      setTimeout(function() { btn.blur(); }, 10);
    });
    
    $('#btn_toolbar .btn').on('blur', function() {
      $(this).removeClass('active');
    });
    
    $(document).on('click', '#accordion > .panel > .panel-heading', function(event) {
      $(this).next('.panel-collapse').collapse('toggle');
      event.stopImmediatePropagation();
    });
    
    // Tooltips for range buttons
    $('#range_x_minus, #range_x_plus, #range_y_minus, #range_y_plus').tooltip({
      container: 'body'
    });
    
    // Load first data
    updateGraphData();
    
    // Stop the application when page is unloaded
    window.onbeforeunload = function() { 
      $.ajax({
        url: stop_app_url,
        async: false
      });
    };
            
  });
  
  function startApp() {
    $.get(
      start_app_url
    )
    .done(function(dresult) {
      if(dresult.status == 'ERROR') {
        showModalError((dresult.reason ? dresult.reason : 'Could not start the application.'), true);
      }
      else {
        $.post(
          post_url, 
          JSON.stringify({ datasets: { params: def_params } })
        )
        .done(function(dresult) {
          app_started = true;
          updateGraphData();      
        })
        .fail(function() {
          showModalError('Could not initialize the application with default parameters.', false, true);
        });
      }
    })
    .fail(function() {
      showModalError('Could not start the application.', true);
    });
  }
  
  function showModalError(err_msg, retry_btn, restart_btn, ignore_btn) {
    var err_modal = $('#modal_err');
    
    err_modal.find('#btn_retry_get')[retry_btn ? 'show' : 'hide']();
    err_modal.find('.btn-app-restart')[restart_btn ? 'show' : 'hide']();
    err_modal.find('#btn_ignore')[ignore_btn ? 'show' : 'hide']();
    err_modal.find('.modal-body').html(err_msg);
    err_modal.modal('show');
  }   
  
  function updateGraphData() {
    if(downloading) {
      return;
    }
    if(update_timer) {
      clearTimeout(update_timer);
      update_timer = null;
    }
    downloading = true;
    
    // Send params if there are any unsent changes
    sendParams();
    
    var arun_before_ajax = autorun;
    var long_timeout_used = use_long_timeout;
    
    $.ajax({
      url: get_url,
      timeout: (use_long_timeout ? long_timeout : request_timeout),
      cache: false
    })
    .done(function(dresult) {
      last_get_failed = false;
      
      if(dresult.status === 'ERROR') {
        if(! app_started) {
          startApp();
        }
        else {
          showModalError((dresult.reason ? dresult.reason : 'Application error.'), true, true);
        }
      }
      else if(dresult.datasets !== undefined && dresult.datasets.params !== undefined) {
        // Check if the application started on the server is the same as on client
        if(app_id !== dresult.app.id) {
          if(! app_started) {
            startApp();
          }
          else {
            $('#new_app_id').text(dresult.app.id);
            $('#modal_app').modal('show');
          }
          return;
        }
        
        app_started = true;
      
        // Check if trigger mode (which may switch autorun) was changed during ajax request
        var arun_after_ajax = autorun;
        
        datasets = [];
        for(var i=0; i<dresult.datasets.g1.length; i++) {
          dresult.datasets.g1[i].color = i;
          dresult.datasets.g1[i].label = 'Channel ' + (i+1);          
          datasets.push(dresult.datasets.g1[i]);
        }        
        dresult.datasets.g1[1].label = 'ISTrp PWR Control';
          
        if(! plot) {
          initPlot(dresult.datasets.params);
        }
        else {
          // Apply the params state received from server if not in edit mode
          if(! user_editing) {
            loadParams(dresult.datasets.params);
            
            // Restore the autorun value modified by loadParams 
            if(arun_before_ajax != arun_after_ajax) {
              autorun = arun_after_ajax; 
            }
          }
          // Time units must be always updated
          else {
            updateTimeUnits(dresult.datasets.params);
          }
          
          // Force X min/max
          if(dresult.datasets.params.forcex_flag == 1) {
            var options = plot.getOptions();
            options.xaxes[0].min = dresult.datasets.params.xmin;
            options.xaxes[0].max = dresult.datasets.params.xmax;
          }
          
          // Redraw the plot using new datasets
          plot.setData(filterData(datasets, plot.width()));
          plot.setupGrid();
          plot.draw();
        }
        
        if(! trig_dragging) {
          updateTriggerSlider();
        }
        
        updateRanges();

        if(autorun || dresult.status === 'AGAIN') {
          if(autorun) {
            $('#btn_single').prop('disabled', true);
          }
          update_timer = setTimeout(function() {
            updateGraphData();
          }, update_interval);
        }
        else {
          $('#btn_single').prop('disabled', false);
        }
      }
      else {
        showModalError('Wrong application data received.', true, true);
      }
    })
    .fail(function(jqXHR, textStatus, errorThrown) {
      if(last_get_failed) {
        showModalError('Data receiving failed.<br>Error status: ' + textStatus, true, true);
        last_get_failed = false;
      }
      else {
        last_get_failed = true;
        downloading = false;
        updateGraphData();  // One more try
      }
    })
    .always(function() {
      if(! last_get_failed) {
        downloading = false;
        if(params.local) {
          // Disable trigger level input if trigger source is External or mode is not Normal
          if(params.original.trig_mode != 1 || params.original.trig_source == 2) {
            $('#trig_level').prop('disabled', true);
            if($('#trig_level').is(':focus')) {
              $('#trig_level').blur();
            }
            $('#apply_trig_level').hide().parent().removeClass('input-group');
          }
          else {
            $('#trig_level').prop('disabled', false);
          }
          
          // Manage the state of other components
          $('#accordion').find('input,select').not('#trig_level').prop('disabled', false);
          $('.btn').not('#btn_single, #range_y_plus, #range_x_plus, #range_x_minus').prop('disabled', false);
        }
      }
      if(long_timeout_used) {
        use_long_timeout = false;
      }
    });
  }
  
  function initPlot(init_params) {
    var plot_holder = $('#plot_holder');
    var ymax = init_params.gui_reset_y_range / 2;
    var ymin = ymax * -1;
    
    // Load received params
    loadParams(init_params);
    
    // When xmin/xmax are null, the min/max values of received data will be used. For ymin/ymax use the gui_reset_y_range param.
    $.extend(true, plot_options, {
      xaxis: { min: null, max: null },
      yaxis: { min: ymin, max: ymax }
    });
    
    // Local optimization    
    var filtered_data = filterData(datasets, plot_holder.width());
  
    // Plot first creation and drawing
    plot = $.plot(
      plot_holder, 
      filtered_data,
      plot_options
    );
    
    // Selection
    plot_holder.on('plotselected', function(event, ranges) {
    
      // Clamp the zooming to prevent eternal zoom
      if(ranges.xaxis.to - ranges.xaxis.from < 0.00001) {
        ranges.xaxis.to = ranges.xaxis.from + 0.00001;
      }
      if(ranges.yaxis.to - ranges.yaxis.from < 0.00001) {
        ranges.yaxis.to = ranges.yaxis.from + 0.00001;
      }
  
      // Do the zooming
      plot = $.plot(
        plot_holder, 
        getData(ranges.xaxis.from, ranges.xaxis.to),
        $.extend(true, plot_options, {
          xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
          yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
        })
      );
      
      params.local.xmin = parseFloat(ranges.xaxis.from.toFixed(xdecimal_places));
      params.local.xmax = parseFloat(ranges.xaxis.to.toFixed(xdecimal_places));
      
      updateTriggerSlider();
      sendParams(true);
    });
    
    // Zoom / Pan
    plot_holder.on('plotzoom plotpan touchmove touchend', function(event) {
      
      if(zoompan_timer) {
        clearTimeout(zoompan_timer);
        zoompan_timer = null;
      }
      
      zoompan_timer = setTimeout(function() {
        zoompan_timer = null;
        
        var xaxis = plot.getAxes().xaxis;
        params.local.xmin = parseFloat(xaxis.min.toFixed(xdecimal_places));
        params.local.xmax = parseFloat(xaxis.max.toFixed(xdecimal_places));
        
        updateTriggerSlider();
        sendParams(true);
                
      }, 250);
    });
  }
  
  function onDropdownChange(that, param_name, do_get) {
    params.local[param_name] = parseInt(that.val());
    sendParams(do_get);
    that.blur();
    user_editing = false;
  }
  
  function loadParams(orig_params) {
    if(! $.isPlainObject(orig_params)) {
      return;
    }
    
    // Ignore xmin/xmax values received from server. That values must be used only on AUTO button click 
    // and on ForceX flag, but that is done before the sendParams() function is called.
    if(plot) {
      var options = plot.getOptions();
      if(options.xaxes[0].min && options.xaxes[0].max) {
        orig_params.xmin = options.xaxes[0].min;
        orig_params.xmax = options.xaxes[0].max;
      }
    }
    
    // Same data in local and original params
    params.original = $.extend({}, orig_params);
    params.local = $.extend({}, params.original);

    // Autorun if trigger mode is Auto(0) or Normal(1), stop if it is Single(2)
    autorun = (params.original.trig_mode == 2 ? 0 : 1);
    
    // Enable the Single button when not in autorun mode
    $('#btn_single').prop('disabled', autorun);
    
    $('#trig_mode').val(params.original.trig_mode);
    $('#trig_source').val(params.original.trig_source);
    $('#trig_edge').val(params.original.trig_edge);
    $('#trig_level').val(floatToLocalString(params.original.trig_level));
    
    if ((refresh_counter++ % meas_panel_dec) == 0) {
        $('#info_ch1_min').html(floatToLocalString(shortenFloat(params.original.meas_min_ch1)));
        $('#info_ch1_max').html(floatToLocalString(shortenFloat(params.original.meas_max_ch1)));
        $('#info_ch1_amp').html(floatToLocalString(shortenFloat(params.original.meas_amp_ch1)));
        $('#info_ch1_avg').html(floatToLocalString(shortenFloat(params.original.meas_avg_ch1)));
        $('#info_ch1_freq').html(convertHz(params.original.meas_freq_ch1));
        $('#info_ch1_period').html(convertSec(params.original.meas_per_ch1));

        $('#info_IST_min').html(floatToLocalString(shortenFloat(params.original.IST_min)));
        $('#info_IST_max').html(floatToLocalString(shortenFloat(params.original.IST_max)));
        $('#info_IST_adj').html(floatToLocalString(shortenFloat(params.original.IST_adj)));
        $('#info_IST_LM35').html(floatToLocalString(shortenFloat(params.original.IST_lm35)));
        $('#info_IST_freq').html(convertHz(params.original.IST_freq));
        $('#info_IST_period').html(convertSec(params.original.IST_per));
    }
    
    $('#gain_ch1_att').val(params.original.prb_att_ch1);
    $('#gain_ch1_sett').val(params.original.gain_ch1);
    $('#gain_ch2_att').val(params.original.prb_att_ch2);
    $('#gain_ch2_sett').val(params.original.gain_ch2);
    
    $('#pid_IST_enable').prop('checked', (params.original.pid_IST_enable ? true : false));

    $('#IST_save2file').prop('checked', (params.original.IST_save2file ? true : false));
    
    $('#pid_IST_DT').val(params.original.pid_IST_DT);
    $('#pid_IST_kp').val(params.original.pid_IST_kp);
    $('#pid_IST_ki').val(params.original.pid_IST_ki);
    $('#pid_IST_kd').val(params.original.pid_IST_kd);
    
    if(params.original.en_avg_at_dec) {
      $('#btn_avg').removeClass('btn-default').addClass('btn-primary');
    }
    else {
      $('#btn_avg').removeClass('btn-primary').addClass('btn-default');
    }
    
    updateTimeUnits(orig_params);
    $('#ytitle').show();
  }
  
  function updateTimeUnits(new_params) {
    if(! $.isPlainObject(new_params)) {
      return;
    } 
    
    params.original.time_units = params.local.time_units = new_params.time_units;

    var timeu_lbl = (params.original.time_units == 0 ? 'μs' : (params.original.time_units == 1 ? 'ms' : 's'));
    $('#xtitle').text('Time [ ' + timeu_lbl + ' ]');
  }
  
  function isParamChanged() {
    if(params.original) {
      for(var key in params.original) {
        if(params.original[key] != params.local[key]) {
          return true;
        }
      }
    }
    return false;
  }
  
  function sendParams(refresh_data, force_send, single_btn) {
    if(sending || (force_send !== true && !isParamChanged())) {
      send_que = sending;
      return;
    }
    
    var auto_flag = params.local.auto_flag;  // Keep the value of auto_flag, because in POST response it is always 0
    sending = true;
    params.local.single_btn = (single_btn === true ? 1 : 0);
    use_long_timeout = !!auto_flag;
  
    $.ajax({
      type: 'POST',
      url: post_url,
      data: JSON.stringify({ datasets: { params: params.local } }),
      timeout: (use_long_timeout ? long_timeout : request_timeout),
      cache: false
    })
    .done(function(dresult) {
      // OK: Load the params received as POST result
      if(dresult.datasets !== undefined && dresult.datasets.params !== undefined) {
      
        // Use the provided min/max values only once after AUTO button was clicked
        if(auto_flag == 1 && dresult.datasets.params.min_y !== dresult.datasets.params.max_y) {
          var options = plot.getOptions();
          
          options.xaxes[0].min = dresult.datasets.params.xmin;
          options.xaxes[0].max = dresult.datasets.params.xmax;
          options.yaxes[0].min = dresult.datasets.params.min_y;
          options.yaxes[0].max = dresult.datasets.params.max_y;
          
          // Enable both channels after click on AUTO button
          if(!$('#btn_ch1').data('checked') || !$('#btn_ch2').data('checked')) {
            $('#btn_ch1').data('checked', true).removeClass('btn-default').addClass('btn-primary');
            $('#btn_ch2').data('checked', true).removeClass('btn-default').addClass('btn-primary');
            redrawPlot();
          }
          // Both channels are already active, do a quick redraw
          else {
            plot.setupGrid();
            plot.draw();
          }
        }
      
        if(auto_flag == 0 && dresult.datasets.params.forcex_flag == 1) {
          var options = plot.getOptions();
          
          options.xaxes[0].min = dresult.datasets.params.xmin;
          options.xaxes[0].max = dresult.datasets.params.xmax;
         
          plot.setupGrid();
          plot.draw();
        }      
      
        loadParams(dresult.datasets.params);
        updateTriggerSlider();
        
        if(refresh_data && !downloading) {
          updateGraphData();
        } 
      }
      else if(dresult.status == 'ERROR') {
        showModalError((dresult.reason ? dresult.reason : 'Error while sending data (E1).'), false, true, true);
        send_que = false;
      }
      else {
        showModalError('Error while sending data (E2).', false, true, true);
      }
    })
    .fail(function() {
      showModalError('Error while sending data (E3).', false, true, true);
    })
    .always(function() {
      sending = false;
      user_editing = false;
      
      if(send_que) {
        send_que = false;
        setTimeout(function(refresh_data) {
          sendParams(refresh_data);
        }, 100);
      }
    });
  }
  
  function getData(from, to) {
    var rangedata = new Array();
    for(var i=0; i<datasets.length; i++) {
      if(! $('#btn_ch' + (i+1)).data('checked')) {
        continue;
      }
      rangedata.push({ color: datasets[i].color, label: datasets[i].label, data: [] });
      for(var j=0; j<datasets[i].data.length; j++) {
        if(datasets[i].data[j][0] > to) {
          break;
        }
        if(datasets[i].data[j][0] >= from) {
          rangedata[rangedata.length - 1].data.push(datasets[i].data[j]);
        }
      }
    }
    rangedata = filterData(rangedata, (plot ? plot.width() : $('plot_holder').width()));
    return rangedata;
  }
  
  // Use only data for selected channels and do downsamling (data decimation), which is required for 
  // better performance. On the canvas cannot be shown too much graph points. 
  function filterData(dsets, points) {
    var filtered = [];
    var num_of_channels = 2;

    for(var l=0; l<num_of_channels; l++) {
      if(! $('#btn_ch' + (l+1)).data('checked')) {
        continue;
      }

      i = Math.min(l, dsets.length - 1);

      filtered.push({ color: dsets[i].color, label: dsets[i].label, data: [] });
      
      if(points_per_px === null || dsets[i].data.length > points * points_per_px) {
        var step = Math.ceil(dsets[i].data.length / (points * points_per_px));
        var k = 0;
        for(var j=0; j<dsets[i].data.length; j++) {
          if(k > 0 && ++k < step) {
            continue;
          }
          filtered[filtered.length - 1].data.push(dsets[i].data[j]);
          k = 1;
        }
      }
      else {
        filtered[filtered.length - 1].data = dsets[i].data.slice(0);
      }
    }
    
    filtered = addTriggerDataSet(filtered);
    return filtered;
  }
  
  // Add a data series for the trigger level line
  function addTriggerDataSet(dsets) {
  
    // Don't add trigger dataset if trigger level is outside the visible area...
    if(plot) {
      var yaxis = plot.getAxes().yaxis;
      if(params.local.trig_level < yaxis.min || params.local.trig_level > yaxis.max) {
        return dsets;
      }
    }
    // ...or trigger mode is not Normal
    if(params.local.trig_mode != 1) {
      return dsets;
    }
    // ...or trigger source is external
    if(params.local.trig_source == 2) {
      return dsets;
    }
    // ...or outside of received data for Y axis
    var ch1ymin = null, 
        ch1ymax = null;
    // Find Y min/max values from data for first visible channel
    for(var i=0; i<dsets[0].data.length; i++) {
      ch1ymin = (ch1ymin === null || ch1ymin > dsets[0].data[i][1] ? dsets[0].data[i][1] : ch1ymin);
      ch1ymax = (ch1ymax === null || ch1ymax < dsets[0].data[i][1] ? dsets[0].data[i][1] : ch1ymax);
    }
    if(dsets.length > 1) {
      var ch2ymin = null, 
          ch2ymax = null;
      // Find Y min/max values from data for second visible channel
      for(var i=0; i<dsets[1].data.length; i++) {
        ch2ymin = (ch2ymin === null || ch2ymin > dsets[1].data[i][1] ? dsets[1].data[i][1] : ch2ymin);
        ch2ymax = (ch2ymax === null || ch2ymax < dsets[1].data[i][1] ? dsets[1].data[i][1] : ch2ymax);
      }
      // Check if trigger level is outside of found values
      if(params.local.trig_level < Math.min(ch1ymin, ch2ymin) || params.local.trig_level > Math.max(ch1ymax, ch2ymax)) {
        return dsets;
      }
    }
    else {
      // Check if trigger level is outside of found values
      if(params.local.trig_level < ch1ymin || params.local.trig_level > ch1ymax) {
        return dsets;
      }
    }
  
    var index = 0;
    var dxmin = 0;
    var dxmax = 1;
    
    if(dsets.length && dsets[0].data[0]) {
      index = dsets.length;
      
      dxmin = dsets[0].data[0][0];
      dxmax = dsets[0].data[dsets[0].data.length - 1][0];
    }
    dsets[index] = { color: 2, data: [[dxmin, params.local.trig_level], [dxmax, params.local.trig_level]], shadowSize: 1 };
    
    return dsets;
  }
  
  function runStop() {
    if(autorun) {
      $('#btn_single').prop('disabled', true);
      updateGraphData();
    }
    else {
      $('#btn_single').prop('disabled', false);
      if(update_timer) {
        clearTimeout(update_timer);
        update_timer = null;
      }
    }
  }
  
  function singleUpdate() {
    if(! autorun) {
      sendParams(true, true, true);
    }
  }
  
  function redrawPlot() {
    if(! downloading) {
      if(! plot) {
        updateGraphData();
      }
      else {
        var options = plot.getOptions();
        plot = $.plot(
          plot.getPlaceholder(), 
          filterData(datasets, plot.width()),
          $.extend(true, plot_options, {
            xaxis: { min: options.xaxes[0].min, max: options.xaxes[0].max },
            yaxis: { min: options.yaxes[0].min, max: options.yaxes[0].max }
          })
        );
        updateTriggerSlider();        
      }
    }
  }
  
  function setVisibleChannels(btn) {
    var other_btn = $(btn.id == 'btn_ch1' ? '#btn_ch2' : '#btn_ch1');
    var btn = $(btn);
    var checked = !btn.data('checked');
    
    btn.data('checked', checked).toggleClass('btn-default btn-primary');
    
    // At least one button must be checked, so that at least one graph will be visible.
    if(! checked) {
      other_btn.data('checked', true).removeClass('btn-default').addClass('btn-primary');
    }
    redrawPlot();
  }
  
  function autoscaleY() {
    if(! plot) {
      return;
    }
    
    var options = plot.getOptions();
    var axes = plot.getAxes();
    
    // Set Y scale to data min/max + 10%
    options.yaxes[0].min = (axes.yaxis.datamin < 0 ? axes.yaxis.datamin * 1.1 : axes.yaxis.datamin - axes.yaxis.datamin * 0.1); 
    options.yaxes[0].max = (axes.yaxis.datamax > 0 ? axes.yaxis.datamax * 1.1 : axes.yaxis.datamax + axes.yaxis.datamax * 0.1);

    plot.setupGrid();
    plot.draw();
    
    updateRanges();
    updateTriggerSlider();
  }
  
  function setAvgAtDec() {
    if(! plot) {
      return;
    }
    
    $('#btn_avg').toggleClass('btn-default btn-primary');

    if($('#btn_avg').hasClass('btn-primary')) {
      params.local.en_avg_at_dec = 1;
    }
    else{
      params.local.en_avg_at_dec = 0;
    }

    sendParams(true, true);
  }

  function resetZoom() {
    if(! plot) {
      return;
    }
    
    $('#btn_ch1, #btn_ch2').data('checked', true).removeClass('btn-default').addClass('btn-primary');
    
    var ymax = params.original.gui_reset_y_range / 2;
    var ymin = ymax * -1;
    
    $.extend(true, plot_options, {
      xaxis: { min: null, max: null },
      yaxis: { min: ymin, max: ymax }
    });
         
    var options = plot.getOptions();
    options.xaxes[0].min = null;
    options.xaxes[0].max = null;
    options.yaxes[0].min = ymin;
    options.yaxes[0].max = ymax;
    
    plot.setupGrid();
    plot.draw();
    
    params.local.xmin = xmin;
    params.local.xmax = xmax;
    
    sendParams(true, true);
  }

  function updateZoom() {
    if(! plot) {
      return;
    }
    
    params.local.xmin = 0;
    params.local.xmax = time_range_max[params.local.time_range];
  
    var axes = plot.getAxes();
    var options = plot.getOptions();
    
    options.xaxes[0].min = params.local.xmin;
    options.xaxes[0].max = params.local.xmax;
    options.yaxes[0].min = axes.yaxis.min;
    options.yaxes[0].max = axes.yaxis.max;
    
    plot.setupGrid();
    plot.draw();

    sendParams(true, true);
  }
  
  function selectTool(toolname) {
    $('#selzoompan .btn').removeClass('btn-primary').addClass('btn-default');
    $(this).toggleClass('btn-default btn-primary');
  
    if(toolname == 'zoomin') {
      enableZoomInSelection();
    }
    if(toolname == 'zoomout') {
      enableZoomOut();
    }
    else if(toolname == 'pan') {
      enablePanning();
    }
  }

  function enableZoomInSelection() {
    if(plot_options.hasOwnProperty('selection')) {
      return;
    }
    
    var plot_pholder = plot.getPlaceholder();

    // Disable panning and zoom out
    delete plot_options.pan;
    plot_pholder.off('click.rp');
    
    // Get current min/max for both axes to use them to fix the current view
    var axes = plot.getAxes();
    
    plot = $.plot(
      plot_pholder, 
      plot.getData(),
      $.extend(true, plot_options, {
        selection: { mode: 'xy' },
        xaxis: { min: axes.xaxis.min, max: axes.xaxis.max },
        yaxis: { min: axes.yaxis.min, max: axes.yaxis.max }
      })
    );
  }
  
  function enableZoomOut() {
    var plot_pholder = plot.getPlaceholder();
    
    plot_pholder.on('click.rp', function(event) {
      var offset = $(event.target).offset();
      
      plot.zoomOut({
        center: { left: event.pageX - offset.left, top: event.pageY - offset.top },
        amount: 1.2
      });
    });
    
    // Disable zoom in selection and panning
    delete plot_options.selection;
    delete plot_options.pan;
    
    // Get current min/max for both axes to use them to fix the current view
    var axes = plot.getAxes();
    
    plot = $.plot(
      plot_pholder, 
      plot.getData(),
      $.extend(true, plot_options, {
        xaxis: { min: axes.xaxis.min, max: axes.xaxis.max },
        yaxis: { min: axes.yaxis.min, max: axes.yaxis.max }
      })
    );
  }
  
  function enablePanning() {
    if(plot_options.hasOwnProperty('pan')) {
      return;
    }
    
    var plot_pholder = plot.getPlaceholder();
    
    // Disable selection zooming and zoom out
    delete plot_options.selection;
    plot_pholder.off('click.rp');
    
    // Get current min/max for both axes to use them to fix the current view
    var axes = plot.getAxes();
    
    plot = $.plot(
      plot_pholder, 
      plot.getData(),
      $.extend(true, plot_options, {
        pan: { interactive: true },
        xaxis: { min: axes.xaxis.min, max: axes.xaxis.max },
        yaxis: { min: axes.yaxis.min, max: axes.yaxis.max }
      })
    );
  }
  
  function mouseDownMove(that, evt) {
    var y;
    user_editing = true;
    
    if(evt.type.indexOf('touch') > -1) {
      y = evt.originalEvent.touches[0].clientY - that.getBoundingClientRect().top - plot.getPlotOffset().top;
      touch_last_y = y;
    }
    else {
      y = evt.clientY - that.getBoundingClientRect().top - plot.getPlotOffset().top;
    }
    updateTriggerSlider(y);
    
    $('#trigger_tooltip').data('bs.tooltip').options.title = plot.getAxes().yaxis.c2p(y).toFixed(trigger_level_xdecimal_places);
    $('#trigger_tooltip').tooltip('show');
  }

  function mouseUpOut(evt) {
    if(trig_dragging) {
      trig_dragging = false;
      
      var y;
      if(evt.type.indexOf('touch') > -1) {
        //y = evt.originalEvent.touches[0].clientY - this.getBoundingClientRect().top - plot.getPlotOffset().top;
        y = touch_last_y;
      }
      else {
        y = evt.clientY - this.getBoundingClientRect().top - plot.getPlotOffset().top;
      }
      
      params.local.trig_level = parseFloat(plot.getAxes().yaxis.c2p(y).toFixed(trigger_level_xdecimal_places));           
      
      updateTriggerSlider();
      redrawPlot();
      sendParams();
    }
    else {
      user_editing = false;
    }
    $('#trigger_tooltip').tooltip('hide');
  }
  
  function updateTriggerSlider(y, update_input) {
    if(! plot) {
      return;
    }
    
    var canvas = $('#trigger_canvas')[0];
    var context = canvas.getContext('2d');
    var plot_offset = plot.getPlotOffset();
    var ymax = params.original.gui_reset_y_range / 2;
    var ymin = ymax * -1;
    
    // If trigger level is outside the predefined ymin/ymax, change the level
    
    if(params.local.trig_level < ymin) {
      params.local.trig_level = ymin;
    }
    else if(params.local.trig_level > ymax) {
      params.local.trig_level = ymax;
    }
    
    if(y === undefined) {
      if(update_input !== false) {
        $('#trig_level').not(':focus').val(floatToLocalString(params.local.trig_level));
      }
      y = plot.getAxes().yaxis.p2c(params.local.trig_level);
    }
    
    // If trigger source is External or mode is not Normal or trigger level is not in visible area, do not show the trigger slider and paint the vertical line with gray
    context.clearRect(0, 0, canvas.width, canvas.height); 
    var yaxis = plot.getAxes().yaxis;
    if(params.original.trig_mode != 1 || params.local.trig_level < yaxis.min || params.local.trig_level > yaxis.max || params.original.trig_source == 2) {
      context.lineWidth = 1;
      context.strokeStyle = '#dddddd';
      context.stroke();
      context.beginPath();
      context.moveTo(10, plot_offset.top);
      context.lineTo(10, canvas.height - plot_offset.bottom + 1);
      context.stroke();
    }
    else {
      context.beginPath();
      context.arc(10, y + plot_offset.top, 8, 0, 2 * Math.PI, false);
      context.fillStyle = '#009900';
      context.fill();
      context.lineWidth = 1;
      context.strokeStyle = '#007700';
      context.stroke();
      context.beginPath();
      context.moveTo(10, plot_offset.top);
      context.lineTo(10, canvas.height - plot_offset.bottom + 1);
      context.stroke();
    }
    $('#trigger_tooltip').css({ top: y + plot_offset.top });  
  }
  
  function updateRanges() {
    var xunit = (params.local.time_units == 0 ? 'μs' : (params.local.time_units == 1 ? 'ms' : 's'));
    var yunit = 'V';
    var axes = plot.getAxes();
    var xrange = axes.xaxis.max - axes.xaxis.min;
    var yrange = axes.yaxis.max - axes.yaxis.min;
    var ymaxrange = params.original.gui_reset_y_range;
    var xmaxrange = 20.0;  // seconds
    var xminrange = 20e-9; // seconds
    var decimals = 0;

    if(xunit == 'μs' && xrange < 1) {
      xrange *= 1000;
      xunit = 'ns';
    }
    if(xrange < 1) {
      decimals = 1;
    }
    var seconds = (xunit == 'ns' ? 1e-9 : ( xunit == 'μs' ? 1e-6 : (xunit == 'ms' ? 1e-3 : 1)));
    
    if(yrange < 1) {
      yunit = 'mV';
      yrange *= 1000;
      ymaxrange *= 1000;
    }

    $('#range_x').html(+(Math.round(xrange + "e+" + decimals) + "e-" + decimals) + ' ' + xunit);
    $('#range_y').html(Math.floor(yrange) + ' ' + yunit);
    
    var nearest_x = getNearestRanges(xrange);
    var nearest_y = getNearestRanges(yrange);
        
    // X limitations 
    if(nearest_x.next * seconds > xmaxrange) {
      nearest_x.next = null;
      $('#range_x_plus').prop('disabled', true);
    }
    else {
      $('#range_x_plus').prop('disabled', false);
    }
    if(nearest_x.prev * seconds < xminrange) {
      nearest_x.prev = null;
      $('#range_x_minus').prop('disabled', true);
    }
    else {
      $('#range_x_minus').prop('disabled', false);
    }
    
    $('#range_x_minus').data({ nearest: nearest_x.prev, unit: xunit }).data('bs.tooltip').options.title = nearest_x.prev;
    $('#range_x_plus').data({ nearest: nearest_x.next, unit: xunit }).data('bs.tooltip').options.title = nearest_x.next;
    
    // Y limitations
    if(nearest_y.next - nearest_y.prev >= ymaxrange) {
      nearest_y.next = null;
      $('#range_y_plus').prop('disabled', true);
    }
    else {
      $('#range_y_plus').prop('disabled', false);
    }
    
    $('#range_y_minus').data({ nearest: nearest_y.prev, unit: yunit }).data('bs.tooltip').options.title = nearest_y.prev;
    $('#range_y_plus').data({ nearest: nearest_y.next, unit: yunit }).data('bs.tooltip').options.title = nearest_y.next;
  }
  
  function getNearestRanges(number) {
    var log10 = Math.floor(Math.log(number) / Math.LN10); 
    var normalized = number / Math.pow(10, log10);    
    var i = 0;
    var prev = null;
    var next = null;
    
    while(i < range_steps.length - 1) {
      var ratio = range_steps[i+1] / normalized;
      if(ratio > 0.99 && ratio < 1.01) {
        prev = range_steps[i];
        next = range_steps[i+2];
        break;
      }
      if(range_steps[i] < normalized && normalized < range_steps[i+1]) {
        prev = range_steps[i];
        next = range_steps[i+1];
        break;
      }
      i++;
    }

    return { 
      prev: prev * Math.pow(10, log10), 
      next: next * Math.pow(10, log10) 
    };
  }
  
  function serverAutoScale() {
    params.local.auto_flag = 1;
    sendParams(true);
  }
        
  function getLocalDecimalSeparator() {
    var n = 1.1;
    return n.toLocaleString().substring(1,2);
  }
  
  function parseLocalFloat(num) {
    return +(num.replace(getLocalDecimalSeparator(), '.'));
  }
  
  function floatToLocalString(num) {
    // Workaround for a bug in Safari 6 (reference: https://github.com/mleibman/SlickGrid/pull/472)
    //return num.toString().replace('.', getLocalDecimalSeparator());
    return (num + '').replace('.', getLocalDecimalSeparator());
  }
  
  function shortenFloat(value) {
    return value.toFixed(Math.abs(value) >= 10 ? 1 : 3);
  }

  function convertHz(value) {
    var unit = '';
    var decsep = getLocalDecimalSeparator();
    
    if(value >= 1e6) {
      value /= 1e6;
      unit = '<span class="unit">MHz</span>';
    }
    else if(value >= 1e3) {
      value /= 1e3;
      unit = '<span class="unit">kHz</span>';
    }
    else {
      unit = '<span class="unit">Hz</span>';
    }

    // Fix to 4 decimal digits in total regardless of the decimal point placement
    var eps = 1e-2;
    if (value >= 100 - eps) {
      value = value.toFixed(1);
    }
    else if  (value >= 10 - eps) {
      value = value.toFixed(2);
    }
    else {
      value = value.toFixed(3);
    }
    
    value = (value == 0 ? '---' + decsep + '-' : floatToLocalString(value));
    return value + unit;
  }
  
  function convertSec(value) {
    var unit = '';
    var decsep = getLocalDecimalSeparator();
    
    if(value < 1e-6) {
      value *= 1e9;
      unit = '<span class="unit">ns</span>';
    }
    else if(value < 1e-3) {
      value *= 1e6;
      unit = '<span class="unit">μs</span>';
    }
    else if(value < 1) {
      value *= 1e3
      unit = '<span class="unit">ms</span>';
    }
    else {
      unit = '<span class="unit">s</span>';
    }

    // Fix to 4 decimal digits in total regardless of the decimal point placement
    var eps = 1e-2;
    if (value >= 100 - eps) {
      value = value.toFixed(1);
    }
    else if  (value >= 10 - eps) {
      value = value.toFixed(2);
    }
    else {
      value = value.toFixed(3);
    }
    
    value = (value == 0 ? '---' + decsep + '-' : floatToLocalString(value));
    return value + unit;
  }
  
  </script>
  <script src="../assets/redpitaya.custom.js"></script>
</head>
<body>
  <div class="header">
    <div class="container">
      <a id="btn_exit" class="pull-left" href="/index.html"><span class="glyphicon glyphicon-chevron-left" title="Exit" alt="Exit"></span></a>
      <img class="logo pull-left" src="../assets/images/logo_white.png">
      <h2 class="page-title">IST RealProbe Flow Sensor measurement System</h2>
    </div>
  </div>
  <div class="container">
    <div class="row">
      <div id="btn_toolbar" class="col-xs-12">
        <button id="btn_autoscale_y" class="btn btn-primary btn-lg" data-autozoom="false" onclick="autoscaleY()">
          <span class="glyphicon glyphicon-resize-vertical"></span> Autoscale
        </button>
        <button class="btn btn-primary btn-lg" onclick="resetZoom()">
          <span class="glyphicon glyphicon-retweet"></span> Reset zoom
        </button>  
        <div id="selzoompan" class="btn-group" data-toggle="buttons">
          <button id="btn_zoomin" class="btn btn-primary" onclick="selectTool.call(this, 'zoomin')" style="display: none">
            <span class="glyphicon glyphicon-zoom-in"></span>
          </button>
          <button id="btn_zoomout" class="btn btn-default" onclick="selectTool.call(this, 'zoomout')" style="display: none">
            <span class="glyphicon glyphicon-zoom-out"></span>
          </button>
          <button id="btn_pan" class="btn btn-default" onclick="selectTool.call(this, 'pan')" style="display: none">
            <span class="glyphicon glyphicon-move"></span>
          </button>
          <button id="btn_zoompan" class="btn btn-primary btn-lg" onclick="selectTool.call(this, 'zoompan')" style="display: none">
            <span class="glyphicon glyphicon-search"></span><span class="glyphicon glyphicon-move"></span>
          </button>
        </div>
        <button id="btn_ch1" class="btn btn-primary btn-lg" data-checked="true" onclick="setVisibleChannels(this)">Channel 1</button>
        <button id="btn_ch2" class="btn btn-primary btn-lg" data-checked="true" onclick="setVisibleChannels(this)">ISTrp PWR Control</button>
        <button id="btn_auto" class="btn btn-primary btn-lg" onclick="serverAutoScale()">AUTO</button>
        <button id="btn_avg" class="btn btn-default btn-lg" onclick="setAvgAtDec()">Averaging</button>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-12 col-sm-12 col-md-8">
        <div class="graph-holder well well-small">
          <div id="ytitle">Voltage [ V ]</div>
          <div id="trigger_cnv_holder">
            <canvas id="trigger_canvas" width="25" height="460"></canvas>
            <div id="trigger_tooltip"></div>
          </div>
          <div id="plot_holder"></div>
          <div id="xtitle"></div>
        </div>
      </div>
      <div class="panel-group col-xs-12 col-sm-12 col-md-4" id="accordion">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h4 class="panel-title">
              <a data-toggle="collapse" href="#trigger">
                Trigger
              </a>
            </h4>
          </div>
          <div id="trigger" class="panel-collapse collapse in">
            <div class="panel-body">
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label for="trig_source" class="col-xs-4 control-label">Source:</label>
                  <div class="col-xs-8">
                    <select id="trig_source" class="form-control">
                      <option value="0">Channel 1</option>
                      <option value="2">External</option>
                    </select>
                  </div>
                </div>
                <div class="form-group">
                  <label for="trig_mode" class="col-xs-4 control-label">Mode:</label>
                  <div class="col-xs-8">
                    <select id="trig_mode" class="form-control">
                      <option value="0">Auto</option>
                      <option value="1">Normal</option>
                      <option value="2">Single</option>
                    </select>
                  </div>
                </div>
                <div class="form-group">
                  <label for="trig_edge" class="col-xs-4 control-label">Edge:</label>
                  <div class="col-xs-8">
                    <select id="trig_edge" class="form-control">
                      <option value="0">Rising</option>
                      <option value="1">Falling</option>
                    </select>
                  </div>
                </div>
                <div class="form-group">
                  <label for="trig_level" class="col-xs-4 control-label">Level:</label>
                  <div class="col-xs-5">
                    <input type="text" id="trig_level" value="0" class="form-control" autocomplete="off">
                    <span id="apply_trig_level" class="input-group-btn" style="display: none;">
                      <button class="btn btn-primary btn-lg" type="button"><span class="glyphicon glyphicon-ok-circle"></span></button>
                    </span>
                  </div>
                  <div id="trig_lev_units" class="col-xs-3" style="padding: 7px 0 0;">V</div>
                </div>
              </form>
              <div class="row">
                <div class="col-xs-4"> </div>
                <div class="col-xs-5">
                  <button id="btn_single" class="btn btn-primary" onclick="singleUpdate()" disabled>
                    <span class="glyphicon glyphicon-step-forward"></span> Single
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="panel panel-default">
          <div class="panel-heading">
            <h4 class="panel-title">
              <a data-toggle="collapse" href="#range">
                Range
              </a>
            </h4>
          </div>
          <div id="range" class="panel-collapse collapse">
            <div class="panel-body">
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label class="col-xs-4 col-sm-3 control-label" style="padding-top: 35px;">Range:</label>
                  <div class="col-xs-8 col-sm-9">
                    <div class="row">
                      <div class="col-xs-6 text-center">
                        <div class="group-label" style="padding-bottom: 10px;">X axis</div>
                        <div class="btn-group" style="margin-bottom: -3px;">
                          <button id="range_x_minus" type="button" class="btn btn-primary btn-lg range-btn-left">
                            <span class="glyphicon glyphicon-minus"></span>
                          </button>
                          <button id="range_x_plus" type="button" class="btn btn-primary btn-lg range-btn-right">
                            <span class="glyphicon glyphicon-plus"></span>
                          </button>
                        </div>
                        <div>
                          <span id="range_x" class="badge range-badge">-</span>
                        </div>
                      </div>
                      <div class="col-xs-6 text-center">
                        <div class="group-label" style="padding-bottom: 10px;">Y axis</div>
                        <div class="btn-group" style="margin-bottom: -3px;">
                          <button id="range_y_minus" type="button" class="btn btn-primary btn-lg range-btn-left">
                            <span class="glyphicon glyphicon-minus"></span>
                          </button>
                          <button id="range_y_plus" type="button" class="btn btn-primary btn-lg range-btn-right">
                            <span class="glyphicon glyphicon-plus"></span>
                          </button>
                        </div>
                        <div>
                          <span id="range_y" class="badge range-badge">-</span>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
                <div class="form-group">
                  <label class="col-xs-4 col-sm-3 control-label">Offset:</label>
                  <div class="col-xs-8 col-sm-9">
                    <div class="row">
                      <div class="col-xs-6 text-center">
                        <div class="btn-group" style="margin-bottom: -3px;">
                          <button id="offset_x_minus" type="button" class="btn btn-primary btn-lg range-btn-left">
                            <span class="glyphicon glyphicon-minus"></span>
                          </button>
                          <button id="offset_x_plus" type="button" class="btn btn-primary btn-lg range-btn-right">
                            <span class="glyphicon glyphicon-plus"></span>
                          </button>
                        </div>
                      </div>
                      <div class="col-xs-6 text-center">
                        <div class="btn-group" style="margin-bottom: -3px;">
                          <button id="offset_y_minus" type="button" class="btn btn-primary btn-lg range-btn-left">
                            <span class="glyphicon glyphicon-minus"></span>
                          </button>
                          <button id="offset_y_plus" type="button" class="btn btn-primary btn-lg range-btn-right">
                            <span class="glyphicon glyphicon-plus"></span>
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
        <div class="panel panel-default">
          <div class="panel-heading">
            <h4 class="panel-title">
              <a data-toggle="collapse" href="#measure">
                Measure
              </a>
            </h4>
          </div>
          <div id="measure" class="panel-collapse collapse">
            <div class="panel-body">
              <div class="row group-label">
                <div class="col-xs-6" style="text-align: center">Channel 1</div>
                <div class="col-xs-6" style="text-align: center">ISTrp PWR Control</div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Min:</div>
                <div class="col-xs-3"><span id="info_ch1_min">-</span><span class="unit">V</span></div>
                <div class="col-xs-3 txt-right">Min:</div>
                <div class="col-xs-3"><span id="info_IST_min">-</span><span class="unit">V</span></div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Max:</div>
                <div class="col-xs-3"><span id="info_ch1_max">-</span><span class="unit">V</span></div>
                <div class="col-xs-3 txt-right">Max:</div>
                <div class="col-xs-3"><span id="info_IST_max">-</span><span class="unit">V</span></div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Amp:</div>
                <div class="col-xs-3"><span id="info_ch1_amp">-</span><span class="unit">Vpp</span></div>
                <div class="col-xs-3 txt-right">RS adj.:</div>
                <div class="col-xs-3"><span id="info_IST_adj">-</span><span class="unit"></span></div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Avg:</div>
                <div class="col-xs-3"><span id="info_ch1_avg">-</span><span class="unit">V</span></div>
                <div class="col-xs-3 txt-right">Temp LM35:</div>
                <div class="col-xs-3"><span id="info_IST_LM35">-</span><span class="unit">°C</span></div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Freq:</div>
                <div class="col-xs-3" id="info_ch1_freq">-<span class="unit">Hz</span></div>
                <div class="col-xs-3 txt-right">PID Freq:</div>
                <div class="col-xs-3" id="info_IST_freq">-<span class="unit">Hz</span></div>
              </div>
              <div class="row">
                <div class="col-xs-3 txt-right">Period:</div>
                <div class="col-xs-3" id="info_ch1_period">-<span class="unit">s</span></div>
                <div class="col-xs-3 txt-right">PID Period:</div>
                <div class="col-xs-3" id="info_IST_period">-<span class="unit">s</span></div>
              </div>
            </div>
          </div>
        </div>
        <div class="panel panel-default">
          <div class="panel-heading">
            <h4 class="panel-title">
              <a data-toggle="collapse" href="#gain">
                Gain settings
              </a>
            </h4>
          </div>
          <div id="gain" class="panel-collapse collapse">
            <div class="panel-body">
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="group-label" style="padding-bottom: 10px;">Channel 1</div>
                <div class="form-group">
                  <label for="gain_ch1_att" class="col-xs-4 col-sm-6 control-label">Probe attenuation:</label>
                  <div class="col-xs-4 col-sm-5">
                    <select class="form-control" id="gain_ch1_att">
                      <option value="0">1x</option>
                      <option value="1">10x</option>
                    </select>
                  </div>
                </div>
                <div class="form-group">
                  <label for="gain_ch1_sett" class="col-xs-4 col-sm-6 control-label">Gain setting:</label>
                  <div class="col-xs-4 col-sm-5">
                    <select class="form-control" id="gain_ch1_sett">
                      <option value="0">LV</option>
                      <option value="1">HV</option>
                    </select>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
        <div class="panel panel-default">
          <div class="panel-heading">
            <h4 class="panel-title">
              <a data-toggle="collapse" href="#pid">
                PID Controller for IST RealProbe
              </a>
            </h4>
          </div>
          <div id="pid" class="panel-collapse collapse">
            <div class="panel-body">
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="checkbox" style="padding-bottom: 12px;">
                  <label class="group-label">
                    <input type="checkbox" id="pid_IST_enable" checked> PID Controller for IST RealProbe
                  </label>
                </div>
              </form>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="checkbox" style="padding-bottom: 12px;">
                  <label class="group-label">
                    <input type="checkbox" id="IST_save2file" checked> Save to File (/tmp)
                  </label>
                </div>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="checkbox" style="padding-bottom: 12px;">
                </div>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label for="pid_IST_DT" class="col-xs-4 control-label">Delta T:</label>
                  <div class="col-xs-4 col-sm-5">
                    <input type="text" autocomplete="off" class="form-control" value="0" id="pid_IST_DT">
                    <span style="display: none;" class="input-group-btn" id="apply_pid_IST_DT">
                      <button type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-ok-circle"></span></button>
                    </span>
                  </div>
                  <div style="padding: 7px 0 0;" class="col-xs-2" id="pid_IST_DT_units">°C</div>
                </div>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label for="pid_IST_kp" class="col-xs-4 control-label">Kp:</label>
                  <div class="col-xs-4 col-sm-5">
                    <input type="text" autocomplete="off" class="form-control" value="0" id="pid_IST_kp">
                    <span style="display: none;" class="input-group-btn" id="apply_pid_IST_kp">
                      <button type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-ok-circle"></span></button>
                    </span>
                  </div>
                  <div style="padding: 7px 0 0;" class="col-xs-2" id="pid_IST_kp_units">cnt</div>
                </div>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label for="pid_IST_ki" class="col-xs-4 control-label">Ki:</label>
                  <div class="col-xs-4 col-sm-5">
                    <input type="text" autocomplete="off" class="form-control" value="0" id="pid_IST_ki">
                    <span style="display: none;" class="input-group-btn" id="apply_pid_IST_ki">
                      <button type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-ok-circle"></span></button>
                    </span>
                  </div>
                  <div style="padding: 7px 0 0;" class="col-xs-2" id="pid_IST_ki_units">cnt</div>
                </div>
              </form>
              <form class="form-horizontal" role="form" onsubmit="return false;">
                <div class="form-group">
                  <label for="pid_IST_kd" class="col-xs-4 control-label">Kd:</label>
                  <div class="col-xs-4 col-sm-5">
                    <input type="text" autocomplete="off" class="form-control" value="0" id="pid_IST_kd">
                    <span style="display: none;" class="input-group-btn" id="apply_pid_IST_kd">
                      <button type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-ok-circle"></span></button>
                    </span>
                  </div>
                  <div style="padding: 7px 0 0;" class="col-xs-2" id="pid_IST_kd_units">cnt</div>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="footer clearfix">
      <p class="pull-right" style="margin: 4px 0 0">&copy; 2014 - Red Pitaya</p>
    </div>
  </div>
  <div id="modal_err" class="modal" tabindex="-1" role="dialog" aria-labelledby="modal_err_label" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4 class="modal-title" id="modal_err_label">Application error</h4>
        </div>
        <div class="modal-body"></div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default btn-close-modal" id="btn_ignore">Ignore</button>
          <button type="button" class="btn btn-default" id="btn_retry_get">Retry</button>
          <button type="button" class="btn btn-primary btn-app-restart">Restart</button>
        </div>
      </div>
    </div>
  </div>
  <div id="modal_app" class="modal" tabindex="-1" role="dialog" aria-labelledby="modal_app_label" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4 class="modal-title" id="modal_app_label">Application stopped</h4>
        </div>
        <div class="modal-body">
          The <strong><span class="app-id"></span></strong> application was stopped. The current started application is <strong><span id="new_app_id"></span></strong>.<br />
          Do you want to switch to newly started application or to restart <strong><span class="app-id"></span></strong>?
        </div>
        <div class="modal-footer">
          <a href="/index.html" class="btn btn-danger pull-left">Exit app</a>
          <button type="button" class="btn btn-default" id="btn_switch_app">Switch</button>
          <button type="button" class="btn btn-primary btn-app-restart">Restart</button>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
